home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 25 / Cream of the Crop 25.iso / os2 / gnuwget.zip / wget-1.4.3 / src / host.c < prev    next >
C/C++ Source or Header  |  1997-02-01  |  14KB  |  514 lines

  1. /* Dealing with host names.
  2.    Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
  3.    
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2 of the License, or
  7.    (at your option) any later version.
  8.    
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.    
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18.  
  19. #ifdef HAVE_CONFIG_H
  20. #  include <config.h>
  21. #endif /* HAVE_CONFIG_H */
  22.  
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <ctype.h>
  26. #ifdef HAVE_STRING_H
  27. #  include <string.h>
  28. #else
  29. #  include <strings.h>
  30. #endif
  31. #include <assert.h>
  32. #include <sys/types.h>
  33.  
  34. #ifdef WINDOWS
  35. # include <winsock.h>
  36. #else
  37. # include <sys/socket.h>
  38. # include <netinet/in.h>
  39. # include <arpa/inet.h>
  40. # include <netdb.h>
  41. #endif /* WINDOWS */
  42.  
  43. #ifdef HAVE_SYS_SYSTEMINFO_H
  44. #  include <sys/systeminfo.h>
  45. #endif
  46.  
  47. #include "wget.h"
  48. #include "options.h"
  49. #include "utils.h"
  50. #include "url.h"
  51. #include "host.h"
  52.  
  53. /* These are often not declared in header files, so I do it. */
  54. #ifndef __cplusplus
  55. int gethostname();
  56. int getdomainname();
  57. #endif /* __cplusplus */
  58.  
  59. extern struct options opt;
  60. host_t *hlist;
  61.  
  62. /* The same as gethostbyname, but supports internet addresses of the
  63.    form N.N.N.N */
  64. struct hostent *
  65. ngethostbyname(const char *name)
  66. {
  67.    struct hostent *hp;
  68.    unsigned long addr;
  69.  
  70.    addr = (unsigned long)inet_addr(name);
  71.    if ((int)addr != -1)
  72.       hp = gethostbyaddr((char *)&addr, sizeof (addr), AF_INET);
  73.    else
  74.       hp = gethostbyname(name);
  75.    return hp;
  76. }
  77.  
  78. /* Store the address of host, internet-style. First check for it in
  79.    the host hash, and (if not found), use ngethostbyname to get it.
  80.  
  81.    The function returns 1 on successful finding of the hostname, 0
  82.    otherwise. */
  83. int
  84. store_hostaddress(unsigned char *where, const char *hostname)
  85. {
  86.    host_t *t;
  87.    unsigned long addr;
  88.    struct hostent *hptr;
  89.    struct in_addr in;
  90.    char *inet_s;
  91.  
  92.    /* If the address is of the form d.d.d.d, there will be no trouble
  93.       with it. */
  94.    addr = (unsigned long)inet_addr(hostname);
  95.    if ((int)addr == -1)
  96.    {
  97.       /* If it is not of that form, try to find it in the cache. */
  98.       t = search_host(hlist, hostname);
  99.       if (t)
  100.      addr = (unsigned long)inet_addr(t->realname);
  101.    }
  102.    /* If we have the numeric address, just store it. */
  103.    if ((int)addr != -1)
  104.    {
  105.       /* This works on both little and big endian, since inet_addr
  106.      returns the address in the proper order.  It appears to work
  107.      on 64-bit machines too. */
  108.       memcpy(where, &addr, 4);
  109.       return 1;
  110.    }
  111.    /* Since all else has failed, let's try gethostbyname. Note that
  112.       it's gethostbyname, not ngethostbyname, since we *know* the
  113.       address is not numerical. */
  114.    if (!(hptr = gethostbyname(hostname)))
  115.       return 0;
  116.    /* Copy the address of the host to socket description. */
  117.    memcpy(where, hptr->h_addr_list[0], hptr->h_length);
  118.    /* Now that we're here, we could as well cache the hostname for
  119.       future use, as in realhost(). First, we have to look for it by
  120.       address to know if it's already in the cache by another name. */
  121.    memcpy(&in.s_addr, *hptr->h_addr_list, sizeof(in.s_addr));
  122.    inet_s = nstrdup(inet_ntoa(in));
  123.    t = search_address(hlist, inet_s);
  124.    if (t) /* Found in the list, as realname. */
  125.    {
  126.       /* Set the default, 0 quality. */
  127.       hlist = add_hlist(hlist, hostname, inet_s, 0);
  128.       free(inet_s);
  129.       return 1;
  130.    }
  131.    /* Since this is really the first time this host is encountered,
  132.       set quality to 1. */
  133.    hlist = add_hlist(hlist, hostname, inet_s, 1);
  134.    free(inet_s);
  135.    return 1;
  136. }
  137.  
  138. /* Add a host to the host list. The list is sorted by addresses. For
  139.    equal addresses, the entries with quality should bubble towards the
  140.    beginning of the list. */
  141. host_t *
  142. add_hlist(host_t *l, const char *nhost, const char *nreal, int quality)
  143. {
  144.    host_t *t, *old, *beg;
  145.    int cmp;
  146.  
  147.    /* The entry goes to the beginning of the list if the list is empty
  148.       or the order requires it. */
  149.    if (!l || (cmp = strcmp(nreal, l->realname) < 0))
  150.    {
  151.       t = (host_t *)nmalloc(sizeof(host_t));
  152.       t->hostname = nstrdup(nhost);
  153.       t->realname = nstrdup(nreal);
  154.       t->quality = quality;
  155.       t->next = l;
  156.       return t;
  157.    }
  158.  
  159.    beg = l;
  160.    /* Second two one-before-the-last element. */
  161.    while (l->next)
  162.    {
  163.       old = l;
  164.       l = l->next;
  165.       cmp = strcmp(nreal, l->realname);
  166.       if (cmp >= 0)
  167.      continue;
  168.       /* If the next list element is greater than s, put s between the
  169.      current and the next list element. */
  170.       t = (host_t *)nmalloc(sizeof(host_t));
  171.       old->next = t;
  172.       t->next = l;
  173.       t->hostname = nstrdup(nhost);
  174.       t->realname = nstrdup(nreal);
  175.       t->quality = quality;
  176.       return beg;
  177.    }
  178.    t = (host_t *)nmalloc(sizeof(host_t));
  179.    t->hostname = nstrdup(nhost);
  180.    t->realname = nstrdup(nreal);
  181.    t->quality = quality;
  182.    /* Insert the new element after the last element. */
  183.    l->next = t;
  184.    t->next = NULL;
  185.    return beg;
  186. }
  187.  
  188. /* Search the linked list by hostname. Return the entry, if found, or
  189.    NULL otherwise. The search is case-insensitive. */
  190. host_t *
  191. search_host(host_t *l, const char *host)
  192. {
  193.    while (l)
  194.    {
  195.       if (strcasecmp(l->hostname, host) == 0)
  196.      return l;
  197.       l = l->next;
  198.    }
  199.    return NULL;
  200. }
  201.  
  202. /* Like search_host, but searches by address. */
  203. host_t *
  204. search_address(host_t *l, const char *address)
  205. {
  206.    int cmp;
  207.  
  208.    while (l)
  209.    {
  210.       if ((cmp = strcmp(l->realname, address)) == 0)
  211.      return l;
  212.       else if (cmp > 0)
  213.      return NULL;
  214.       l = l->next;
  215.    }
  216.    return NULL;
  217. }
  218.  
  219. /* This routine frees memory allocated by host linked list. */
  220. void
  221. free_hlist(host_t *l)
  222. {
  223.    host_t *p;
  224.  
  225.    while (l)
  226.    {
  227.       p = l->next;
  228.       free(l->hostname);
  229.       free(l->realname);
  230.       free(l);
  231.       l = p;
  232.    }
  233. }
  234.  
  235.  
  236. /* The routine used to determine the "real" name of the host, as
  237.    viewed by the program. If host is referenced by more than one name,
  238.    "real" name is considered to be the first one.
  239.    
  240.    If the host cannot be found in the list of already dealed-with
  241.    hosts, try with its INET address. If this fails too, add it to the
  242.    list.  The routine does not call gethostbyname twice for the same
  243.    host if it can possibly avoid it. */
  244. char *
  245. realhost(const char *host)
  246. {
  247.    host_t *l;
  248.    struct in_addr in;
  249.    struct hostent *hptr;
  250.    char *inet_s;
  251.  
  252. #ifdef DEBUG
  253.    if (opt.debug)
  254.       fprintf(opt.lfile, "Checking for %s.\n", host);
  255. #endif
  256.    /* Look for the host, looking by the host name. */
  257.    l = search_host(hlist, host);
  258.    if (l && l->quality)              /* Found it with quality */
  259.    {
  260.       DEBUGP("It was already used, by that name.\n");
  261.       return nstrdup(host);
  262.    }
  263.    else if (!l)                      /* Not found, with or without quality */
  264.    {
  265.       /* The fact that gethostbyname will get called makes it
  266.      necessary to store it to the list, to ensure that
  267.      gethostbyname will not be called twice for the same
  268.      string. However, the quality argument must be set
  269.      appropriately.
  270.      
  271.      Note that add_hlist must be called *after* the realname
  272.      search, or the quality would be always set to 0 */
  273.       DEBUGP("This is the first time I hear about that host by that name.\n");
  274.       hptr = ngethostbyname(host);
  275.       if (!hptr)
  276.      return nstrdup(host);
  277.       memcpy(&in.s_addr, *hptr->h_addr_list, sizeof(in.s_addr));
  278.       inet_s = nstrdup(inet_ntoa(in));
  279.    }
  280.    else /* Found, without quality */
  281.    {
  282.       /* This case happens when host is on the list,
  283.      but not as first entry (the one with quality).
  284.      Then we just get its INET address and pick
  285.      up the first entry with quality. */
  286.       DEBUGP("We've dealt with this host, but under a different name.\n");
  287.       inet_s = nstrdup(l->realname);
  288.    }
  289.    
  290.    /* Now we certainly have the INET address. The following
  291.       loop is guaranteed to pick either an entry with
  292.       quality (because it is the first), or nothing. */
  293.    l = search_address(hlist, inet_s);
  294.    if (l) /* Found in the list, as realname. */
  295.    {
  296.       /* Set the default, 0 quality. */
  297.       hlist = add_hlist(hlist, host, inet_s, 0);
  298.       free(inet_s);
  299.       return nstrdup(l->hostname);
  300.    }
  301.    /* Since this is really the first time this host is encountered,
  302.       set quality to 1. */
  303.    hlist = add_hlist(hlist, host, inet_s, 1);
  304.    free(inet_s);
  305.    return nstrdup(host);
  306. }
  307.  
  308. /* This routine compares two hostnames (out of URL-s if the arguments
  309.    are URL-s), taking care of aliases.  It uses realhost to determine
  310.    a unique hostname for each of two hosts. If simple_check is set,
  311.    only strcmp is called. */
  312. int
  313. same_host(const char *u1, const char *u2)
  314. {
  315.    const char *s;
  316.    char *p1, *p2;
  317.    char *real1, *real2;
  318.  
  319.    /* Skip protocol, if present. */
  320.    u1 += skip_url(u1);
  321.    u2 += skip_url(u2);
  322.    u1 += skip_proto(u1);
  323.    u2 += skip_proto(u2);
  324.    
  325.    /* Skip username ans password, if present. */
  326.    u1 += skip_uname(u1);
  327.    u2 += skip_uname(u2);
  328.    
  329.    for (s = u1; *u1 && *u1 != '/' && *u1 != ':'; u1++);
  330.    p1 = strdupdelim(s, u1);
  331.    for (s = u2; *u2 && *u2 != '/' && *u2 != ':'; u2++);
  332.    p2 = strdupdelim(s, u2);
  333. #ifdef DEBUG
  334.    if (opt.debug)
  335.       fprintf(opt.lfile, "Comparing hosts %s and %s...\n", p1, p2);
  336. #endif
  337.    if (!strcasecmp(p1, p2))
  338.    {
  339.       free(p1);
  340.       free(p2);
  341.       DEBUGP("They are quite alike.\n");
  342.       return 1;
  343.    }
  344.    else if (opt.simple_check)
  345.    {
  346.       free(p1);
  347.       free(p2);
  348.       DEBUGP("Since checking is simple, I'd say they are not the same.\n");
  349.       return 0;
  350.    }
  351.    real1 = realhost(p1);
  352.    real2 = realhost(p2);
  353.    free(p1);
  354.    free(p2);
  355.    if (strcasecmp(real1, real2) == 0)
  356.    {
  357.       DEBUGP("They are alike.\n");
  358.       free(real1);
  359.       free(real2);
  360.       return 1;
  361.    }
  362.    else
  363.    {
  364.       DEBUGP("They are not the same.\n");
  365.       free(real1);
  366.       free(real2);
  367.       return 0;
  368.    }
  369. }
  370.  
  371. /* Determine whether a URL is acceptable to be followed,
  372.    according to a list of domains to accept */
  373. int
  374. accept_domain(urlinfo *u)
  375. {
  376.    if (opt.domains)
  377.    {
  378.       assert(u->host != NULL);
  379.       if (!sufmatch((const char **)opt.domains, u->host))
  380.      return 0;
  381.    }
  382.    if (opt.exclude_domains)
  383.    {
  384.       assert(u->host != NULL);
  385.       if (sufmatch((const char **)opt.exclude_domains, u->host))
  386.      return 0;
  387.    }
  388.    return 1;
  389. }
  390.  
  391. /* Check whether a host is matched in the list of domains */
  392. int
  393. sufmatch(const char **list, const char *what)
  394. {
  395.    int i, j, k, lw;
  396.  
  397.    lw = strlen(what);
  398.    for (i = 0; list[i]; i++)
  399.    {
  400.       for (j = strlen(list[i]), k = lw; j >= 0 && k >= 0; j--, k--)
  401.      if (tolower(list[i][j]) != tolower(what[k]))
  402.         break;
  403.       /* The domain must be first to reach to beginning. */
  404.       if (j == -1)
  405.      return 1;
  406.    }
  407.    return 0;
  408. }
  409.  
  410. /* Get email address of the form username@host.domain suitable for
  411.    anonymous FTP passwords. If you have problems, hard-code your
  412.    hostname and domainname by defining MY_HOST and MY_DOMAIN in
  413.    config.h.
  414.  
  415.    If none of the available methods of getting host name and domain
  416.    name works, the function returns username@. If it cannot get
  417.    username, the program quits. */
  418. char *
  419. ftp_getaddress(void)
  420. {
  421.    static char address[256];
  422.    int i, pos;
  423.    static int first = 1;
  424.  
  425.    /* Do it only the first time, since it won't change. */
  426.    if (first)
  427.    {
  428.       first = 0;
  429.       if (!mycuserid(address))
  430.       {
  431.      if (!opt.quiet)
  432.         fprintf(stderr, "Cannot determine user-id.\n");
  433.      exit(1);
  434.       }
  435.       i = strlen(address);
  436.       address[i++] = '@';
  437.       address[i] = '\0';
  438.       pos = i;
  439.       /* pos holds the position after '@'. */
  440. #ifdef MY_HOST
  441.       strcpy(address + i, MY_HOST);
  442. #elif defined(HAVE_GETHOSTNAME)
  443.       if (gethostname(address + i, 256 - i - 1) < 0)
  444.      return address;
  445. #elif defined(HAVE_SYSINFO)
  446.       if (sysinfo(SI_HOSTNAME, address + i, 256 - i - 1) < 0)
  447.      return address;
  448. #else
  449.  #error Cannot determine hostname
  450. #endif
  451.       i = strlen(address);
  452.       /* If we have the dot somewhere in the address, it probably
  453.      means we have the whole domain (hopefully!).  */
  454.       if (strchr(address + pos, '.'))
  455.      return address;
  456. #ifdef MY_DOMAIN
  457.       if (*MY_DOMAIN != '.')  /* Check for leading dot. */
  458.      address[i] = '.';
  459.       ++i;
  460.       strcat(address, MY_DOMAIN);
  461. #elif defined(HAVE_GETDOMAINNAME)
  462.       address[i++] = '.';
  463.       if (getdomainname(address + i, 256 - i - 1) < 0)
  464.       {
  465.      address[pos] = '\0';
  466.      return address;
  467.       }
  468. #elif defined(HAVE_SYSINFO)
  469.       address[i++] = '.';
  470.       if (sysinfo(SI_SRPC_DOMAIN, address + i, 256 - i - 1) < 0)
  471.       {
  472.      address[pos] = '\0';
  473.      return address;
  474.       }
  475. #else
  476.  #error Cannot determine domainname
  477. #endif
  478.       /* Check for various invalid/null domains. */
  479.       if (!*(address + i)
  480.       || !strcasecmp(address + i, "null")
  481.       || !strcasecmp(address + i, "(null)")
  482.       || !strcasecmp(address + i, "(none)"))
  483.       {
  484.      address[pos] = '\0';
  485.      return address;
  486.       }
  487.    }
  488.    return address;
  489. }
  490.  
  491. /* Print error messages for host errors. */
  492. char *
  493. herrmsg(int error)
  494. {
  495.    char *msg;
  496.  
  497.    /* Can't use switch since some constants are equal. */
  498.    if (error == HOST_NOT_FOUND || error == NO_RECOVERY
  499.        || error == NO_DATA || error == NO_ADDRESS
  500.        || error == TRY_AGAIN)
  501.       msg = "Host not found";
  502.    else
  503.       msg = "Unknown error";
  504.    return msg;
  505. }
  506.  
  507. /* Clean the host list. */
  508. void
  509. clean_hosts(void)
  510. {
  511.    free_hlist(hlist);
  512.    hlist = NULL;
  513. }
  514.